package com.lhl.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @author hanLin.liu
 * @create 2024-10-28 16:15
 */
public class NIOServer {

    public static void main(String[] args) throws Exception {

        try(Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();){
            // 绑定监听端口号
            serverSocketChannel.bind(new InetSocketAddress(8888));

            // 设置非阻塞
            serverSocketChannel.configureBlocking(false);

            // 注册接收连接事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("等待连接...");
            while (true) {
                // 【阻塞】监听是否有事件发生
                int count = selector.select();
                if (count > 0) {
                    // 发生事件后获取就绪key集合，拿到有新连接后的Channel对应的SelectionKey
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();

                        // isAcceptable() 是用于判断通道是否可以接受新连接的状态方法【处理新连接事件】
                        if (key.isAcceptable()) {

//                        当key.isAcceptable为true时，调用key.channel()，返回的channel和我们上面定义的其实是同一个
//                        ServerSocketChannel server = (ServerSocketChannel) key.channel();

                            // 服务端channel接收客户端SocketChannel
                            SocketChannel clientSocketChannel = serverSocketChannel.accept();

                            clientSocketChannel.configureBlocking(false);

                            // 注册读事件，读事件发生时，会将对应的selectionKey加入到就绪集合中
                            clientSocketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT);
                            System.out.printf("连接成功！接收【%s】的连接\n", clientSocketChannel.getRemoteAddress());
                        }

                        // 用于判断key的Channel是否已准备好读取。【处理读事件】
                        if(key.isReadable()){
                            // 处理读操作
                            handleRead(key);
                            SocketChannel socketChannel = (SocketChannel) key.channel();
                            // 设置可写，接下来将进行写操作
                            socketChannel.register(selector, SelectionKey.OP_WRITE);

                        }
                        // 用于判断key的Channel是否已准备好写。【处理写事件】
                        if(key.isWritable()){
                            SocketChannel socketChannel = (SocketChannel) key.channel();
                            socketChannel.write(ByteBuffer.wrap("我已经处理你的消息".getBytes(StandardCharsets.UTF_8)));

                            // 将客户端通道注册到 Selector 并重新监听读事件，不然这里会一直写，值得缓冲区满
                            socketChannel.register(selector, SelectionKey.OP_READ);
                        }
                        iterator.remove();
                    }
                }

            }
        }
    }


    /**
     * 处理读事件
     * @param key
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();

        // 创建缓冲区对象【默认是写模式】
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        int byteSize = channel.read(byteBuffer);
        if(byteSize > 0){
            // 切换读模式
            byteBuffer.flip();
            String msg = new String(byteBuffer.array(), 0, byteSize);
            System.out.printf("处理线程:【%s】--处理来自【%s】消息：【", Thread.currentThread().getName(), channel.getRemoteAddress());
            System.out.println(msg + "】");

        }else if(byteSize == -1){
            System.out.printf("关闭【%s】的连接\n", channel.getRemoteAddress());
            // 关闭连接
            channel.close();
            // 取消注册
            key.channel();
        }
    }
}
